Skip to content

FELIX-6759 - PROTOTYPE: use plurl for URL handler factory multiplexing - #552

Draft
paulrutter wants to merge 7 commits into
feature/FELIX-6759-Java-25-LTSfrom
feature/FELIX-6759-plurl
Draft

FELIX-6759 - PROTOTYPE: use plurl for URL handler factory multiplexing#552
paulrutter wants to merge 7 commits into
feature/FELIX-6759-Java-25-LTSfrom
feature/FELIX-6759-plurl

Conversation

@paulrutter

Copy link
Copy Markdown
Contributor

Warning

Draft / prototype — not for merge. There is an unresolved licensing question below that blocks this as it stands.

Prototype exploring @tjwatson's suggestion in #433 (comment) that Felix and Equinox should share a cooperative mechanism for the java.net.URL factory singletons. Branches off, and targets, #433.

The problem

URLHandlers claims the JVM-wide URLStreamHandlerFactory by reflectively swapping a private static field on java.net.URL, via SecureAction.swapStaticFieldIfNotClass. Obtaining a MethodHandles.Lookup trusted enough to do that is the only remaining reason the framework uses sun.misc.Unsafe — the staticFieldOffset/getObject steal of IMPL_LOOKUP. It also means whichever framework installs itself last wins the singleton, so Felix and Equinox cannot coexist in one JVM without clobbering each other.

To be clear about urgency: this is not a JDK 25 blocker. That Unsafe block is guarded by catch (Throwable) and falls back to AccessibleObject.setAccessible, so on JDK 25 it only produces the familiar warning. This is about JDK 26+ and about framework interop.

What this does

  • Vendors the 11 plurl sources from eclipse-osgi-technology/plurl (commit 6581777) into org.apache.felix.framework.plurl. The only change is the package rename; every license header and copyright notice is preserved verbatim. This mirrors what Equinox did in Plurl API for managing Java singleton factories in URL eclipse-equinox/equinox#848.
  • Adds PlurlURLHandlers, adapting URLHandlers to PlurlStreamHandlerFactory / PlurlContentHandlerFactory. Plurl installs one cooperative router through the supported URL.setURLStreamHandlerFactory API and routes by asking each registered factory shouldHandle(Class) — which maps directly onto what URLHandlers.getFrameworkFromContext() already does by hand, and needs no Unsafe.

Verified on JDK 25: the framework compiles and its test results are unchanged.

⚠️ Licensing — @tjwatson could you confirm?

Every vendored source file declares:

SPDX-License-Identifier: EPL-2.0
Copyright (c) 2025 IBM Corporation

but plurl's own LICENSE file and its pom.xml both declare Apache-2.0. (Its NOTICE also still refers to "slf4j-osgi", which looks like a copy-paste leftover.)

This matters because of where it is being copied to. EPL-2.0 is Category B at the ASF and must not be included in an Apache source release. Equinox was free to vendor these files because Eclipse projects are EPL-2.0 natively; Felix is not.

Given the repo's LICENSE and pom say Apache-2.0, my guess is the EPL headers are carried over from the original Equinox code rather than intended. Could you confirm the intended license and, if it is Apache-2.0, correct the source headers upstream? That would unblock the vendoring approach for Felix and anyone else.

If the headers are intentionally EPL-2.0, then Felix cannot vendor the sources and the options become:

  1. consume plurl as a released binary dependency under the Category B rules — which additionally needs plurl published to Maven Central, as it currently has no release or tag; or
  2. write an Apache-2.0 implementation of the same design in Felix, which works but forks the effort rather than converging on the shared library.

cc @stbischof @laeubi @mattrpav

🤖 Generated with Claude Code

Not for merge. This explores the suggestion in
#433 (comment) that Felix and
Equinox should share a cooperative way to manage the java.net.URL factory singletons.

URLHandlers currently claims the JVM-wide URLStreamHandlerFactory by reflectively
swapping a private static field on java.net.URL
(SecureAction.swapStaticFieldIfNotClass). Getting a MethodHandles.Lookup trusted
enough to do that is the only remaining reason the framework uses sun.misc.Unsafe,
and it means the last framework to install itself wins the singleton, so two
frameworks cannot coexist in one JVM without clobbering each other.

Plurl installs a single cooperative router through the supported
URL.setURLStreamHandlerFactory API and lets any number of parties register with it;
each registered factory answers shouldHandle(Class) to claim the calling class. That
maps onto what URLHandlers.getFrameworkFromContext() already does by hand, and it
needs no Unsafe.

- Vendors the 11 plurl sources from eclipse-osgi-technology/plurl (commit 6581777)
  into org.apache.felix.framework.plurl. The only change is the package rename;
  every license header and copyright notice is preserved verbatim. This mirrors
  eclipse-equinox/equinox#848, which vendored the same files
  into org.eclipse.equinox.plurl.
- Adds PlurlURLHandlers, adapting URLHandlers to PlurlStreamHandlerFactory and
  PlurlContentHandlerFactory, with shouldHandle(Class) recognising this framework's
  bundle class loaders.

UNRESOLVED LICENSING QUESTION: every vendored file declares
"SPDX-License-Identifier: EPL-2.0" and "Copyright (c) 2025 IBM Corporation", while
plurl's own LICENSE and pom.xml declare Apache-2.0. EPL-2.0 is Category B at the ASF
and may not be included in an Apache source release, so this cannot be merged as it
stands. See framework/src/main/java/org/apache/felix/framework/plurl/README.md.

Verified on JDK 25: the framework compiles and its tests are unchanged
(the same 8 pre-existing platform specific failures as before this commit).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
paulrutter added a commit that referenced this pull request Aug 29, 2026
The pull_request trigger filters on the BASE branch, and only accepted master. A
stacked pull request - one opened against another feature branch rather than master -
therefore matched neither trigger and got no CI at all, which is what happened to
PR #552.

Accept feature/** and maintenance/** as a base as well, so stacked work is built
before it is merged down.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@paulrutter

Copy link
Copy Markdown
Contributor Author

CI is red on purpose — Apache RAT rejects the vendored sources

Now that stacked PRs get CI (the pull_request trigger only accepted master as a base, so this PR was getting no build at all until that was fixed on the base branch), the build runs — and fails on all four JDKs in exactly the way that matters:

[INFO] Rat check: Summary over all files. Unapproved: 12, unknown: 12, generated: 0, approved: 243 licenses.
[ERROR] Failed to execute goal org.apache.rat:apache-rat-plugin:0.15:check (default)
        on project org.apache.felix.framework: Too many files with unapproved license: 12

All 12 flagged files are the vendored plurl sources — the 11 .java files plus the README.md. Nothing else in the framework is flagged.

This is the licensing question answering itself. apache-rat-plugin is the ASF's own compliance gate, and it will not accept SPDX-License-Identifier: EPL-2.0 headers in an Apache source tree. It is enforcing the Category B rule automatically. Equinox could vendor these files because Eclipse projects are EPL-2.0 natively; Felix cannot.

The failure is being left in place deliberately. It could be silenced with a RAT exclusion for plurl/**, but that would suppress a genuine compliance violation rather than resolve it, and would leave a green tick implying the licensing was settled when it is not. Red is the accurate state: this branch cannot merge as it stands.

So the technical side is done and the remaining blocker is purely licensing:

  • the framework compiles and its tests are unchanged with plurl vendored and PlurlURLHandlers wired in;
  • the only thing standing in the way is the header discrepancy — plurl's LICENSE and pom.xml say Apache-2.0, while every source file says EPL-2.0 with Copyright (c) 2025 IBM Corporation.

@tjwatson — if the EPL headers are carried over from the original Equinox code rather than intended, correcting them upstream to match plurl's own Apache-2.0 LICENSE and pom would turn this red build green and unblock the approach for Felix and anyone else wanting to adopt the library. If they are intentionally EPL-2.0, then Felix needs one of the alternatives listed in the PR description instead.

🤖 Generated with Claude Code

@tjwatson

Copy link
Copy Markdown
Member

The intention when I moved the source to osgi-technology was that I would use the apache header like this:

https://github.com/eclipse-osgi-technology/osgi-test/blob/696b03cd2eaf4e91f7ad0e3d424e4bebb28525ce/org.osgi.test.assertj.feature/src/test/java/org/osgi/test/assertj/feature/AssertionsTest.java#L1-L17

Somehow that was overlooked when setting up the source in https://github.com/eclipse-osgi-technology/plurl

It should be a non-issue to get the headers updated. But what I ultimately wanted to do was have a release out of the osgi-technology project for plurl that would be used by both Equinox and Felix unchanged, in the original package there.

@paulrutter

Copy link
Copy Markdown
Contributor Author

The intention when I moved the source to osgi-technology was that I would use the apache header like this:

https://github.com/eclipse-osgi-technology/osgi-test/blob/696b03cd2eaf4e91f7ad0e3d424e4bebb28525ce/org.osgi.test.assertj.feature/src/test/java/org/osgi/test/assertj/feature/AssertionsTest.java#L1-L17

Somehow that was overlooked when setting up the source in https://github.com/eclipse-osgi-technology/plurl

It should be a non-issue to get the headers updated. But what I ultimately wanted to do was have a release out of the osgi-technology project for plurl that would be used by both Equinox and Felix unchanged, in the original package there.

Thanks for your quick reply! Let's first see if we can get consensus on the JDK 25 PR, then we can revisit this one.

Would it be possible to get plurl released as a separate artifact, with the right headers, so we can use it without copying the code into Felix?

Apart from that, does this PR make sense to you?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants